package task5;

import java.util.ArrayList;
import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class Bank {
	
	private static final int DEFAULT_FRACTURES_IN_MONEY = 100;

	class TransactionThread implements Runnable {

		int accountFromId;
		int accountToId;
		Money amountToTransfer;

		TransactionThread(int from, int to, Money amount) {
			accountFromId = from;
			accountToId = to;
			amountToTransfer = new Money(amount);
		}

		public void run() {
			transfer(accountFromId, accountToId, amountToTransfer);
		}
	}

	class Money implements Comparable<Money> {
		private int wholes = 0;
		private int fracs = 0;

		Money(int whole, int frac) {
			if (whole >= 0) {
				wholes = whole;
			} else {
				wholes = 0;
			}

			if (frac >= 0) {
				fracs = frac;
			} else {
				frac = 0;
			}
		}

		Money(Money arg) {
			wholes = arg.wholes;
			fracs = arg.fracs;
		}

		void add(Money income) {
			fracs += income.fracs;
			if (fracs >= DEFAULT_FRACTURES_IN_MONEY) {
				wholes++;
				fracs -= DEFAULT_FRACTURES_IN_MONEY;
			}
			wholes += income.wholes;
		}

		Money substract(Money outcome) {
			Money res;

			if (this.compareTo(outcome) <= 0) {
				res = new Money(this);
				wholes = 0;
				fracs = 0;
				return res;
			}

			if (fracs < outcome.fracs) {
				wholes--;
				fracs += DEFAULT_FRACTURES_IN_MONEY;
				fracs -= outcome.fracs;
			}
			wholes -= outcome.wholes;

			res = new Money(outcome);
			return res;
		}

		public int compareTo(Money arg) {
			if (wholes == arg.wholes && fracs == arg.fracs) {
				return 0;
			}

			if (wholes < arg.wholes || (wholes == arg.wholes && fracs < arg.fracs)) {
				return -1;
			}

			return 1;
		}

		public String toString() {
			return (wholes + "." + fracs);
		}
	}

	class Account {
		private ReentrantReadWriteLock mRWLock = new ReentrantReadWriteLock();

		private int ID;
		private Money balance;

		Account(int id, Money startingBalance) {
			ID = id;
			balance = new Money(startingBalance);
		}

		public Money getBalance() {
			Money value;

			mRWLock.readLock().lock();
			try {
				value = new Money(balance);
				return value;
			} finally {
				mRWLock.readLock().unlock();
			}
		}

		public void deposit(Money amount) {
			mRWLock.writeLock().lock();

	        try {
	        	balance.add(amount);
	        } finally {
	        	mRWLock.writeLock().unlock();
	        }
		}

		public Money withdraw(Money amount) {
			Money value;

			mRWLock.writeLock().lock();

	        try {
	        	value = new Money(balance.substract(amount));
	        	return value;
	        } finally {
	        	mRWLock.writeLock().unlock();
	        }
		}
	}

	private Money totalFundsInBank = new Money(0, 0);

	private ArrayList<Account> mAccounts = new ArrayList<Account>();	

	public void createAccount(int ID, Money startBalance) {
		mAccounts.add(new Account(ID, startBalance));
		totalFundsInBank.add(startBalance);
	}

	public int getAccountsQuantity() {
		return mAccounts.size();
	}

	public void transfer(int fromId, int toId, Money amount) {		
		mAccounts.get(toId).deposit(mAccounts.get(fromId).withdraw(amount));		
	}

	public Money getTotalFundsInBank() {
		return new Money(totalFundsInBank);
	}
}